3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile
+4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py
+4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend/lib/console.py
+4054a301VEag2GwrBrFBna5U1BGlLA tools/xend/lib/main.py
+4055ad9ah9IuC3sJT2c_gYIFY5Tw_g tools/xend/lib/manager.py
+40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/lib/utils.c
4054a2fdkdATEnRw-U7AUlgu-6JiUA tools/xend/setup.py
-4054a301VEag2GwrBrFBna5U1BGlLA tools/xend/xend.py
-40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/xend_utils.c
403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
4050c413PhhLNAYk3TEwP37i_iLw9Q tools/xentrace/xentrace.8
403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
install: all
if [ "$(prefix)" = "" ]; then python setup.py install; \
else python setup.py install --root "$(prefix)"; fi
- install --mode=755 xend.py $(prefix)/usr/sbin
- ln -sf xend.py $(prefix)/usr/sbin/xend
+ install --mode=755 xend $(prefix)/usr/sbin
dist: all
mkdir -p ../../../../install/lib/python
install --mode=755 $$i ../../../../install/lib/python/`basename $$i` ; \
done
python -c 'import py_compile, sys; py_compile.compile("XenoUtil.py")'
- install --mode=755 xend.py ../../../../install/sbin
- ln -sf xend.py ../../../../install/sbin/xend
+ install --mode=755 xend ../../../../install/sbin
clean:
rm -rf build *.pyc *.pyo *.a *.so *.o *~ *.rpm
--- /dev/null
+
+#############################################################
+## xend/console.py -- Console-management functions for Xend
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+#############################################################
+
+import errno, re, os, select, signal, socket, struct, sys
+
+
+##
+## interface:
+## Each control interface owns an instance of this class, which manages
+## the current state of the console interface. Normally a console interface
+## will be one of two state:
+## LISTENING: listening for a connection on TCP port 'self.port'
+## CONNECTED: sending/receiving console data on TCP port 'self.port'
+##
+## A dictionary of all active interfaces, indexed by TCP socket descriptor,
+## is accessible as 'interface.interface_list'.
+##
+## NB. When a class instance is to be destroyed you *must* call the 'close'
+## method. Otherwise a stale reference will eb left in the interface list.
+##
+class interface:
+
+ # The various states that a console interface may be in.
+ CLOSED = 0 # No console activity
+ LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'.
+ CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'.
+
+
+ # Dictionary of all active (non-closed) console interfaces.
+ interface_list = {}
+
+
+ # NB. 'key' is an opaque value that has no meaning in this class.
+ def __init__(self, port, key):
+ self.status = interface.CLOSED
+ self.port = port
+ self.key = key
+
+
+ # Is this interface closed (inactive)?
+ def closed(self):
+ return self.status == interface.CLOSED
+
+
+ # Is this interface listening?
+ def listening(self):
+ return self.status == interface.LISTENING
+
+
+ # Is this interface active and connected?
+ def connected(self):
+ return self.status == interface.CONNECTED
+
+
+ # Close the interface, if it is not closed already.
+ def close(self):
+ if not self.closed():
+ del interface.interface_list[self.sock.fileno()]
+ self.sock.close()
+ del self.sock
+ self.status = interface.CLOSED
+
+
+ # Move the interface into the 'listening' state. Opens a new listening
+ # socket and updates 'interface_list'.
+ def listen(self):
+ # Close old socket (if any), and create a fresh one.
+ self.close()
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+
+ try:
+ # Turn the new socket into a non-blocking listener.
+ self.sock.setblocking(False)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack('ii', 0, 0))
+ self.sock.bind(('', self.port))
+ self.sock.listen(1)
+
+ # Announce the new status of thsi interface.
+ self.status = interface.LISTENING
+ interface.interface_list[self.sock.fileno()] = self
+
+ except:
+ # In case of trouble ensure we get rid of dangling socket reference
+ self.sock.close()
+ del self.sock
+ raise
+
+
+ # Move a listening interface into the 'connected' state.
+ def connect(self):
+ # Pick up a new connection, if one is available.
+ try:
+ (sock, addr) = self.sock.accept()
+ except:
+ return 0
+ sock.setblocking(False)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack('ii', 0, 0))
+
+ # Close the listening socket.
+ self.sock.close()
+
+ # Publish the new socket and the new interface state.
+ self.sock = sock
+ self.status = interface.CONNECTED
+ interface.interface_list[self.sock.fileno()] = self
+ return 1
+
+
--- /dev/null
+
+###########################################################
+## xend.py -- Xen controller daemon
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+###########################################################
+
+import errno, re, os, pwd, select, signal, socket, struct, sys, tempfile, time
+import xend.console, xend.manager, xend.utils, Xc
+
+
+# The following parameters could be placed in a configuration file.
+PID = '/var/run/xend.pid'
+LOG = '/var/log/xend.log'
+USER = 'root'
+CONTROL_DIR = '/var/run/xend'
+UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR
+
+
+def daemon_loop():
+ # Could we do this more nicely? The xend.manager functions need access
+ # to this global state to do their work.
+ global control_list, notifier
+
+ # List of all control interfaces, indexed by local event-channel port.
+ control_list = {}
+
+ xc = Xc.new()
+
+ # Ignore writes to disconnected sockets. We clean up differently.
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+
+ # Construct the management interface. This is a UNIX domain socket via
+ # which we receive 'request' datagrams. Each request is a string that
+ # can be eval'ed as a Python statement. Responses can be remotely eval'ed
+ # by the requester to create a Python dictionary of result values.
+ management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+ if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK):
+ os.unlink(CONTROL_DIR+'/'+UNIX_SOCK)
+ management_interface.setblocking(False)
+ management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK)
+
+ # Interface via which we receive event notifications from other guest
+ # OSes. This interface also allows us to clear/acknowledge outstanding
+ # notifications --- successive notifications for the same channel are
+ # dropped until the first notification is cleared.
+ notifier = xend.utils.notifier()
+
+ ##
+ ## MAIN LOOP
+ ##
+ while 1:
+
+ # Construct a poll set. We wait on:
+ # 1. Requests on the management interface.
+ # 2. Incoming event-channel notifications.
+ # Furthermore, for each active control interface:
+ # 3. Incoming console data.
+ # 4. Space for outgoing console data (if there is data to send).
+ waitset = select.poll()
+ waitset.register(management_interface, select.POLLIN)
+ waitset.register(notifier, select.POLLIN)
+ for idx, (port, rbuf, wbuf, con_if) in control_list.items():
+ if not con_if.closed():
+ pflags = select.POLLIN
+ if not rbuf.empty() and con_if.connected():
+ pflags = select.POLLIN | select.POLLOUT
+ waitset.register(con_if.sock.fileno(), pflags)
+
+ # Wait for something to do...
+ fdset = waitset.poll()
+
+ # Look for messages on the management interface.
+ # These should consist of executable Python statements that call
+ # well-known management functions (e.g., new_control_interface(dom=9)).
+ try:
+ data, addr = management_interface.recvfrom(2048)
+ except socket.error, error:
+ if error[0] != errno.EAGAIN:
+ raise
+ else:
+ if addr:
+ # Evaluate the request in an exception-trapping sandbox.
+ try:
+ print "Mgmt_req[%s]: %s" % (addr, data)
+ response = str(eval('xend.manager.'+data))
+
+ except:
+ # Catch all exceptions and turn into an error response:
+ # status: False
+ # error_type: 'exception'
+ # exception_type: name of exception type.
+ # exception value: textual exception value.
+ exc_type, exc_val = sys.exc_info()[:2]
+ response = { 'success': False }
+ response['error_type'] = 'exception'
+ response['exception_type'] = str(exc_type)
+ response['exception_value'] = str(exc_val)
+ response = str(response)
+
+ # Try to send a response to the requester.
+ try:
+ print "Mgmt_rsp[%s]: %s" % (addr, response)
+ management_interface.sendto(response, addr)
+ except socket.error, error:
+ pass
+
+ # Do work for every console interface that hit in the poll set.
+ for (fd, events) in fdset:
+ if not xend.console.interface.interface_list.has_key(fd):
+ continue
+ con_if = xend.console.interface.interface_list[fd]
+
+ # If the interface is listening, check for pending connections.
+ if con_if.listening():
+ con_if.connect()
+
+ # All done if the interface is not connected.
+ if not con_if.connected():
+ continue
+ (port, rbuf, wbuf, con_if) = control_list[con_if.key]
+
+ # Send as much pending data as possible via the socket.
+ while not rbuf.empty():
+ try:
+ bytes = con_if.sock.send(rbuf.peek())
+ if bytes > 0:
+ rbuf.discard(bytes)
+ except socket.error, error:
+ pass
+
+ # Read as much data as is available. Don't worry about
+ # overflowing our buffer: it's more important to read the
+ # incoming data stream and detect errors or closure of the
+ # remote end in a timely manner.
+ try:
+ while 1:
+ data = con_if.sock.recv(2048)
+ # Return of zero means the remote end has disconnected.
+ # We therefore return the console interface to listening.
+ if not data:
+ con_if.listen()
+ break
+ wbuf.write(data)
+ except socket.error, error:
+ # Assume that most errors mean that the connection is dead.
+ # In such cases we return the interface to 'listening' state.
+ if error[0] != errno.EAGAIN:
+ print "Better return to listening"
+ con_if.listen()
+ print "New status: " + str(con_if.status)
+
+ # We may now have pending data to send via the relevant
+ # inter-domain control interface. If so then we send all we can
+ # and notify the remote end.
+ work_done = False
+ while not wbuf.empty() and port.space_to_write_request():
+ msg = xend.utils.message(0, 0, 0)
+ msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
+ port.write_request(msg)
+ work_done = True
+ if work_done:
+ port.notify()
+
+ # Process control-interface notifications from other guest OSes.
+ while 1:
+ # Grab a notification, if there is one.
+ notification = notifier.read()
+ if not notification:
+ break
+ (idx, type) = notification
+
+ # If we pick up a disconnect notification then we do any necessary
+ # cleanup, even if the event channel doesn't belong to us.
+ # This is intended to prevent the event-channel port space from
+ # getting clogged with stale connections.
+ if type == notifier.DISCONNECT:
+ ret = xc.evtchn_status(idx)
+ if ret['status'] != 'connected':
+ notifier.clear(idx, notifier.NORMAL)
+ notifier.clear(idx, notifier.DISCONNECT)
+ if control_list.has_key(idx):
+ (port, rbuf, wbuf, con_if) = control_list[idx]
+ con_if.close()
+ del control_list[idx], port, rbuf, wbuf, con_if
+ elif ret['status'] == 'disconnected':
+ # There's noone to do the closure for us...
+ xc.evtchn_close(idx)
+
+ # A standard notification: probably means there are messages to
+ # read or that there is space to write messages.
+ elif type == notifier.NORMAL and control_list.has_key(idx):
+ (port, rbuf, wbuf, con_if) = control_list[idx]
+ work_done = False
+
+ # We clear the notification before doing any work, to avoid
+ # races.
+ notifier.clear(idx, notifier.NORMAL)
+
+ # Read incoming requests. Currently assume that request
+ # message always containb console data.
+ while port.request_to_read():
+ msg = port.read_request()
+ rbuf.write(msg.get_payload())
+ port.write_response(msg)
+ work_done = True
+
+ # Incoming responses are currently thrown on the floor.
+ while port.response_to_read():
+ msg = port.read_response()
+ work_done = True
+
+ # Send as much pending console data as there is room for.
+ while not wbuf.empty() and port.space_to_write_request():
+ msg = xend.utils.message(0, 0, 0)
+ msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
+ port.write_request(msg)
+ work_done = True
+
+ # Finally, notify the remote end of any work that we did.
+ if work_done:
+ port.notify()
+
+
+
+def cleanup_daemon(kill=False):
+ # No cleanup to do if the PID file is empty.
+ if not os.path.isfile(PID) or not os.path.getsize(PID):
+ return 0
+ # Read the PID of the previous invocation and search active process list.
+ pid = open(PID, 'r').read()
+ lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+ for line in lines:
+ if re.search('^ *' + pid + '.+xend', line):
+ if not kill:
+ print "Daemon is already running (PID %d)" % int(pid)
+ return 1
+ # Old daemon is still active: terminate it.
+ os.kill(int(pid), 1)
+ # Delete the, now stale, PID file.
+ os.remove(PID)
+ return 0
+
+
+
+def start_daemon():
+ if cleanup_daemon(kill=False):
+ return 1
+
+ if not os.path.exists(CONTROL_DIR):
+ os.mkdir(CONTROL_DIR)
+
+ # Open log file. Truncate it if non-empty, and request line buffering.
+ if os.path.isfile(LOG):
+ os.rename(LOG, LOG+'.old')
+ logfile = open(LOG, 'w+', 1)
+
+ # Detach from TTY.
+ os.setsid()
+
+ # Set the UID.
+ try:
+ os.setuid(pwd.getpwnam(USER)[2])
+ except KeyError, error:
+ print "Error: no such user '%s'" % USER
+ return 1
+
+ # Ensure that zombie children are automatically reaped.
+ xend.utils.autoreap()
+
+ # Fork -- parent writes the PID file and exits.
+ pid = os.fork()
+ if pid:
+ pidfile = open(PID, 'w')
+ pidfile.write(str(pid))
+ pidfile.close()
+ return 0
+
+ # Close down standard file handles
+ try:
+ os.close(0) # stdin
+ os.close(1) # stdout
+ os.close(2) # stderr
+ except:
+ pass
+
+ # Redirect output to log file, then enter the main loop.
+ sys.stdout = sys.stderr = logfile
+ daemon_loop()
+ return 0
+
+
+
+def stop_daemon():
+ return cleanup_daemon(kill=True)
--- /dev/null
+
+#############################################################
+## xend/manager.py -- Management-interface functions for Xend
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+#############################################################
+
+import xend.console, xend.main, xend.utils
+
+
+##
+## new_control_interface:
+## Create a new control interface with the specified domain 'dom'.
+## The console port may also be specified; otehrwise a suitable port is
+## automatically allocated.
+##
+def new_control_interface(dom, console_port=-1):
+ # Allocate an event channel. Clear pending notifications.
+ port = xend.utils.port(dom)
+ xend.main.notifier.clear(port.local_port, xend.main.notifier.NORMAL)
+ xend.main.notifier.clear(port.local_port, xend.main.notifier.DISCONNECT)
+
+ # If necessary, compute a suitable TCP port for console I/O.
+ if console_port < 0:
+ console_port = 9600 + port.local_port
+
+ # Create a listenign console interface.
+ con_if = xend.console.interface(console_port, port.local_port)
+ con_if.listen()
+
+ # Add control state to the master list.
+ xend.main.control_list[port.local_port] = \
+ (port, xend.utils.buffer(), xend.utils.buffer(), con_if)
+
+ # Construct the successful response to be returned to the requester.
+ response = { 'success': True }
+ response['local_port'] = port.local_port
+ response['remote_port'] = port.remote_port
+ response['console_port'] = console_port
+ return response
--- /dev/null
+/******************************************************************************
+ * utils.c
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#include <Python.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <xc.h>
+#include <asm-xeno/control_if.h>
+
+/* NB. The following should be kept in sync with the kernel's evtchn driver. */
+#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
+#define EVTCHN_DEV_MAJOR 10
+#define EVTCHN_DEV_MINOR 200
+#define PORT_NORMAL 0x0000 /* A standard event notification. */
+#define PORT_DISCONNECT 0x8000 /* A port-disconnect notification. */
+#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */
+#define EVTCHN_RESET _IO('E', 1) /* Clear notification buffer. Clear errors. */
+
+/* Size of a machine page frame. */
+#define PAGE_SIZE 4096
+
+
+/*
+ * *********************** NOTIFIER ***********************
+ */
+
+typedef struct {
+ PyObject_HEAD;
+ int evtchn_fd;
+} xu_notifier_object;
+
+static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ u16 v;
+ int bytes;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ if ( errno == EAGAIN )
+ goto none;
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ if ( bytes == sizeof(v) )
+ return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK);
+
+ none:
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_clear(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ u16 v;
+ int idx, type;
+
+ if ( !PyArg_ParseTuple(args, "ii", &idx, &type) )
+ return NULL;
+
+ v = (u16)idx | (u16)type;
+
+ (void)write(xun->evtchn_fd, &v, sizeof(v));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ return PyInt_FromLong(xun->evtchn_fd);
+}
+
+static PyMethodDef xu_notifier_methods[] = {
+ { "read",
+ (PyCFunction)xu_notifier_read,
+ METH_VARARGS,
+ "Read a (@port, @type) pair.\n" },
+
+ { "clear",
+ (PyCFunction)xu_notifier_clear,
+ METH_VARARGS,
+ "Clear a (@port, @type) pair.\n" },
+
+ { "fileno",
+ (PyCFunction)xu_notifier_fileno,
+ METH_VARARGS,
+ "Return the file descriptor for the notification channel.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_notifier_type;
+
+static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
+
+ xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
+ if ( xun->evtchn_fd == -1 )
+ {
+ PyObject_Del((PyObject *)xun);
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ return (PyObject *)xun;
+}
+
+static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
+{
+ if ( strcmp(name, "DISCONNECT") == 0 )
+ return PyInt_FromLong(PORT_DISCONNECT);
+ if ( strcmp(name, "NORMAL") == 0 )
+ return PyInt_FromLong(PORT_NORMAL);
+ return Py_FindMethod(xu_notifier_methods, obj, name);
+}
+
+static void xu_notifier_dealloc(PyObject *self)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ (void)close(xun->evtchn_fd);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_notifier_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "notifier",
+ sizeof(xu_notifier_object),
+ 0,
+ xu_notifier_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_notifier_getattr, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL /* tp_hash */
+};
+
+
+
+/*
+ * *********************** MESSAGE ***********************
+ */
+
+typedef struct {
+ PyObject_HEAD;
+ control_msg_t msg;
+} xu_message_object;
+
+static PyObject *xu_message_append_payload(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+ char *str;
+ int len;
+
+ if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+ return NULL;
+
+ if ( (len + xum->msg.length) > sizeof(xum->msg.msg) )
+ {
+ PyErr_SetString(PyExc_RuntimeError, "out of space in control message");
+ return NULL;
+ }
+
+ memcpy(&xum->msg.msg[xum->msg.length], str, len);
+ xum->msg.length += len;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length);
+}
+
+static PyObject *xu_message_set_header(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+ int type = -1, subtype = -1, id = -1;
+
+ static char *kwd_list[] = { "type", "subtype", "id", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list,
+ &type, &subtype, &id) )
+ return NULL;
+
+ if ( type != -1 )
+ xum->msg.type = (u8)type;
+ if ( subtype != -1 )
+ xum->msg.subtype = (u8)subtype;
+ if ( id != -1 )
+ xum->msg.id = (u8)id;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_message_get_header(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum = (xu_message_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ return Py_BuildValue("{s:i,s:i,s:i}",
+ "type", xum->msg.type,
+ "subtype", xum->msg.subtype,
+ "id", xum->msg.id);
+}
+
+static PyMethodDef xu_message_methods[] = {
+ { "append_payload",
+ (PyCFunction)xu_message_append_payload,
+ METH_VARARGS,
+ "Append @str to the message payload.\n" },
+
+ { "get_payload",
+ (PyCFunction)xu_message_get_payload,
+ METH_VARARGS,
+ "Return the message payload in string form.\n" },
+
+ { "set_header",
+ (PyCFunction)xu_message_set_header,
+ METH_VARARGS | METH_KEYWORDS,
+ "Accepts keywords @type, @subtype, and @id.\n" },
+
+ { "get_header",
+ (PyCFunction)xu_message_get_header,
+ METH_VARARGS,
+ "Returns a dictionary of values for @type, @subtype, and @id.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_message_type;
+
+static PyObject *xu_message_new(PyObject *self, PyObject *args)
+{
+ xu_message_object *xum;
+ int type, subtype, id;
+
+ if ( !PyArg_ParseTuple(args, "iii", &type, &subtype, &id) )
+ return NULL;
+
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+
+ xum->msg.type = type;
+ xum->msg.subtype = subtype;
+ xum->msg.id = id;
+ xum->msg.length = 0;
+
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_message_getattr(PyObject *obj, char *name)
+{
+ xu_message_object *xum;
+ if ( strcmp(name, "MAX_PAYLOAD") == 0 )
+ return PyInt_FromLong(sizeof(xum->msg.msg));
+ return Py_FindMethod(xu_message_methods, obj, name);
+}
+
+static void xu_message_dealloc(PyObject *self)
+{
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_message_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "message",
+ sizeof(xu_message_object),
+ 0,
+ xu_message_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_message_getattr, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL /* tp_hash */
+};
+
+
+
+/*
+ * *********************** PORT ***********************
+ */
+
+static control_if_t *map_control_interface(int fd, unsigned long pfn)
+{
+ char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, pfn * PAGE_SIZE);
+ if ( vaddr == MAP_FAILED )
+ return NULL;
+ return (control_if_t *)(vaddr + 2048);
+}
+static void unmap_control_interface(int fd, control_if_t *c)
+{
+ char *vaddr = (char *)c - 2048;
+ (void)munmap(vaddr, PAGE_SIZE);
+}
+
+typedef struct {
+ PyObject_HEAD;
+ int mem_fd;
+ int xc_handle;
+ u64 remote_dom;
+ int local_port, remote_port;
+ control_if_t *interface;
+ CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
+ CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
+} xu_port_object;
+
+static PyObject *port_error;
+
+static PyObject *xu_port_notify(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX c = xup->tx_req_cons;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->tx_req_prod) ||
+ ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+ {
+ PyErr_SetString(port_error, "no request to read");
+ return NULL;
+ }
+
+ cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+ memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+ if ( xum->msg.length > sizeof(xum->msg.msg) )
+ xum->msg.length = sizeof(xum->msg.msg);
+ xup->tx_req_cons++;
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX p = xup->rx_req_prod;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+ return NULL;
+
+ if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+ {
+ PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message");
+ return NULL;
+ }
+
+ if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+ {
+ PyErr_SetString(port_error, "no space to write request");
+ return NULL;
+ }
+
+ cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
+ memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+ xup->rx_req_prod = cif->rx_req_prod = p + 1;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX c = xup->rx_resp_cons;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+ {
+ PyErr_SetString(port_error, "no response to read");
+ return NULL;
+ }
+
+ cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
+ xum = PyObject_New(xu_message_object, &xu_message_type);
+ memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+ if ( xum->msg.length > sizeof(xum->msg.msg) )
+ xum->msg.length = sizeof(xum->msg.msg);
+ xup->rx_resp_cons++;
+ return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ xu_message_object *xum;
+ CONTROL_RING_IDX p = xup->tx_resp_prod;
+ control_if_t *cif = xup->interface;
+ control_msg_t *cmsg;
+
+ if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+ return NULL;
+
+ if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+ {
+ PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message");
+ return NULL;
+ }
+
+ if ( p == xup->tx_req_cons )
+ {
+ PyErr_SetString(port_error, "no space to write response");
+ return NULL;
+ }
+
+ cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
+ memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+ xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX c = xup->tx_req_cons;
+ control_if_t *cif = xup->interface;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->tx_req_prod) ||
+ ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX p = xup->rx_req_prod;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX c = xup->rx_resp_cons;
+ control_if_t *cif = xup->interface;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_response(
+ PyObject *self, PyObject *args)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ CONTROL_RING_IDX p = xup->tx_resp_prod;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( p == xup->tx_req_cons )
+ return PyInt_FromLong(0);
+
+ return PyInt_FromLong(1);
+}
+
+static PyMethodDef xu_port_methods[] = {
+ { "notify",
+ (PyCFunction)xu_port_notify,
+ METH_VARARGS,
+ "Send a notification to the remote end.\n" },
+
+ { "read_request",
+ (PyCFunction)xu_port_read_request,
+ METH_VARARGS,
+ "Read a request message from the control interface.\n" },
+
+ { "write_request",
+ (PyCFunction)xu_port_write_request,
+ METH_VARARGS,
+ "Write a request message to the control interface.\n" },
+
+ { "read_response",
+ (PyCFunction)xu_port_read_response,
+ METH_VARARGS,
+ "Read a response message from the control interface.\n" },
+
+ { "write_response",
+ (PyCFunction)xu_port_write_response,
+ METH_VARARGS,
+ "Write a response message to the control interface.\n" },
+
+ { "request_to_read",
+ (PyCFunction)xu_port_request_to_read,
+ METH_VARARGS,
+ "Returns TRUE if there is a request message to read.\n" },
+
+ { "space_to_write_request",
+ (PyCFunction)xu_port_space_to_write_request,
+ METH_VARARGS,
+ "Returns TRUE if there is space to write a request message.\n" },
+
+ { "response_to_read",
+ (PyCFunction)xu_port_response_to_read,
+ METH_VARARGS,
+ "Returns TRUE if there is a response message to read.\n" },
+
+ { "space_to_write_response",
+ (PyCFunction)xu_port_space_to_write_response,
+ METH_VARARGS,
+ "Returns TRUE if there is space to write a response message.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_port_type;
+
+static PyObject *xu_port_new(PyObject *self, PyObject *args)
+{
+ xu_port_object *xup;
+ u64 dom;
+ int port1, port2;
+ xc_dominfo_t info;
+
+ if ( !PyArg_ParseTuple(args, "L", &dom) )
+ return NULL;
+
+ xup = PyObject_New(xu_port_object, &xu_port_type);
+
+ if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
+ {
+ PyErr_SetString(port_error, "Could not open '/dev/mem'");
+ goto fail1;
+ }
+
+ if ( (xup->xc_handle = xc_interface_open()) == -1 )
+ {
+ PyErr_SetString(port_error, "Could not open Xen control interface");
+ goto fail2;
+ }
+
+ if ( xc_evtchn_open(xup->xc_handle, DOMID_SELF, dom, &port1, &port2) != 0 )
+ {
+ PyErr_SetString(port_error, "Could not open channel to domain");
+ goto fail3;
+ }
+
+ if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) ||
+ (info.domid != dom) )
+ {
+ PyErr_SetString(port_error, "Failed to obtain domain status");
+ goto fail4;
+ }
+
+ xup->interface =
+ map_control_interface(xup->mem_fd, info.shared_info_frame);
+ if ( xup->interface == NULL )
+ {
+ PyErr_SetString(port_error, "Failed to map domain control interface");
+ goto fail4;
+ }
+
+ xup->tx_req_cons = 0;
+ xup->tx_resp_prod = 0;
+ xup->rx_req_prod = 0;
+ xup->rx_resp_cons = 0;
+ xup->remote_dom = dom;
+ xup->local_port = port1;
+ xup->remote_port = port2;
+
+ return (PyObject *)xup;
+
+
+ fail4:
+ (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
+ fail3:
+ (void)xc_interface_close(xup->xc_handle);
+ fail2:
+ (void)close(xup->mem_fd);
+ fail1:
+ PyObject_Del((PyObject *)xup);
+ return NULL;
+}
+
+static PyObject *xu_port_getattr(PyObject *obj, char *name)
+{
+ xu_port_object *xup = (xu_port_object *)obj;
+ if ( strcmp(name, "local_port") == 0 )
+ return PyInt_FromLong(xup->local_port);
+ if ( strcmp(name, "remote_port") == 0 )
+ return PyInt_FromLong(xup->remote_port);
+ if ( strcmp(name, "remote_dom") == 0 )
+ return PyLong_FromUnsignedLongLong(xup->remote_dom);
+ return Py_FindMethod(xu_port_methods, obj, name);
+}
+
+static void xu_port_dealloc(PyObject *self)
+{
+ xu_port_object *xup = (xu_port_object *)self;
+ unmap_control_interface(xup->mem_fd, xup->interface);
+ (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
+ (void)xc_interface_close(xup->xc_handle);
+ (void)close(xup->mem_fd);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_port_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "port",
+ sizeof(xu_port_object),
+ 0,
+ xu_port_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_port_getattr, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL /* tp_hash */
+};
+
+
+
+/*
+ * *********************** BUFFER ***********************
+ */
+
+#define BUFSZ 65536
+#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1))
+typedef unsigned int BUF_IDX;
+
+typedef struct {
+ PyObject_HEAD;
+ char *buf;
+ unsigned int prod, cons;
+} xu_buffer_object;
+
+static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max)
+{
+ PyObject *str1, *str2;
+ int len1, len2, c = MASK_BUF_IDX(xub->cons);
+
+ len1 = xub->prod - xub->cons;
+ if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */
+ len1 = BUFSZ - c;
+ if ( len1 > max ) /* clip to specified maximum */
+ len1 = max;
+ if ( len1 < 0 ) /* sanity */
+ len1 = 0;
+
+ if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL )
+ return NULL;
+
+ if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) )
+ {
+ len2 = max - len1;
+ if ( len2 > MASK_BUF_IDX(xub->prod) )
+ len2 = MASK_BUF_IDX(xub->prod);
+ if ( len2 > 0 )
+ {
+ str2 = PyString_FromStringAndSize(&xub->buf[0], len2);
+ if ( str2 == NULL )
+ return NULL;
+ PyString_ConcatAndDel(&str1, str2);
+ if ( str1 == NULL )
+ return NULL;
+ }
+ }
+
+ return str1;
+}
+
+static PyObject *xu_buffer_peek(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ int max = 1024;
+
+ if ( !PyArg_ParseTuple(args, "|i", &max) )
+ return NULL;
+
+ return __xu_buffer_peek(xub, max);
+}
+
+static PyObject *xu_buffer_read(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ PyObject *str;
+ int max = 1024;
+
+ if ( !PyArg_ParseTuple(args, "|i", &max) )
+ return NULL;
+
+ if ( (str = __xu_buffer_peek(xub, max)) != NULL )
+ xub->cons += PyString_Size(str);
+
+ return str;
+}
+
+static PyObject *xu_buffer_discard(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ int max, len;
+
+ if ( !PyArg_ParseTuple(args, "i", &max) )
+ return NULL;
+
+ len = xub->prod - xub->cons;
+ if ( len > max )
+ len = max;
+ if ( len < 0 )
+ len = 0;
+
+ xub->cons += len;
+
+ return PyInt_FromLong(len);
+}
+
+static PyObject *xu_buffer_write(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ char *str;
+ int len, len1, len2;
+
+ if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+ return NULL;
+
+ len1 = len;
+ if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+ len1 = BUFSZ - MASK_BUF_IDX(xub->prod);
+ if ( len1 > (BUFSZ - (xub->prod - xub->cons)) )
+ len1 = BUFSZ - (xub->prod - xub->cons);
+
+ if ( len1 == 0 )
+ return PyInt_FromLong(0);
+
+ memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1);
+ xub->prod += len1;
+
+ if ( len1 < len )
+ {
+ len2 = len - len1;
+ if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+ len2 = BUFSZ - MASK_BUF_IDX(xub->prod);
+ if ( len2 > (BUFSZ - (xub->prod - xub->cons)) )
+ len2 = BUFSZ - (xub->prod - xub->cons);
+ if ( len2 != 0 )
+ {
+ memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2);
+ xub->prod += len2;
+ return PyInt_FromLong(len1 + len2);
+ }
+ }
+
+ return PyInt_FromLong(len1);
+}
+
+static PyObject *xu_buffer_empty(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( xub->cons == xub->prod )
+ return PyInt_FromLong(1);
+
+ return PyInt_FromLong(0);
+}
+
+static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ if ( (xub->prod - xub->cons) == BUFSZ )
+ return PyInt_FromLong(1);
+
+ return PyInt_FromLong(0);
+}
+
+static PyMethodDef xu_buffer_methods[] = {
+ { "peek",
+ (PyCFunction)xu_buffer_peek,
+ METH_VARARGS,
+ "Peek up to @max bytes from the buffer. Returns a string.\n" },
+
+ { "read",
+ (PyCFunction)xu_buffer_read,
+ METH_VARARGS,
+ "Read up to @max bytes from the buffer. Returns a string.\n" },
+
+ { "discard",
+ (PyCFunction)xu_buffer_discard,
+ METH_VARARGS,
+ "Discard up to @max bytes from the buffer. Returns number of bytes.\n" },
+
+ { "write",
+ (PyCFunction)xu_buffer_write,
+ METH_VARARGS,
+ "Write @string into buffer. Return number of bytes written.\n" },
+
+ { "empty",
+ (PyCFunction)xu_buffer_empty,
+ METH_VARARGS,
+ "Return TRUE if the buffer is empty.\n" },
+
+ { "full",
+ (PyCFunction)xu_buffer_full,
+ METH_VARARGS,
+ "Return TRUE if the buffer is full.\n" },
+
+ { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_buffer_type;
+
+static PyObject *xu_buffer_new(PyObject *self, PyObject *args)
+{
+ xu_buffer_object *xub;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ xub = PyObject_New(xu_buffer_object, &xu_buffer_type);
+
+ if ( (xub->buf = malloc(BUFSZ)) == NULL )
+ {
+ PyObject_Del((PyObject *)xub);
+ return NULL;
+ }
+
+ xub->prod = xub->cons = 0;
+
+ return (PyObject *)xub;
+}
+
+static PyObject *xu_buffer_getattr(PyObject *obj, char *name)
+{
+ return Py_FindMethod(xu_buffer_methods, obj, name);
+}
+
+static void xu_buffer_dealloc(PyObject *self)
+{
+ xu_buffer_object *xub = (xu_buffer_object *)self;
+ free(xub->buf);
+ PyObject_Del(self);
+}
+
+static PyTypeObject xu_buffer_type = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "buffer",
+ sizeof(xu_buffer_object),
+ 0,
+ xu_buffer_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ xu_buffer_getattr, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL /* tp_hash */
+};
+
+
+
+/*
+ * *********************** MODULE WRAPPER ***********************
+ */
+
+static void handle_child_death(int dummy)
+{
+ while ( waitpid(-1, NULL, WNOHANG) > 0 )
+ continue;
+}
+
+static PyObject *xu_autoreap(PyObject *self, PyObject *args)
+{
+ struct sigaction sa;
+
+ if ( !PyArg_ParseTuple(args, "") )
+ return NULL;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handle_child_death;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef xu_methods[] = {
+ { "notifier", xu_notifier_new, METH_VARARGS,
+ "Create a new notifier." },
+ { "message", xu_message_new, METH_VARARGS,
+ "Create a new communications message." },
+ { "port", xu_port_new, METH_VARARGS,
+ "Create a new communications port." },
+ { "buffer", xu_buffer_new, METH_VARARGS,
+ "Create a new ring buffer." },
+ { "autoreap", xu_autoreap, METH_VARARGS,
+ "Ensure that zombie children are automatically reaped by the OS." },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC initutils(void)
+{
+ PyObject *m, *d;
+
+ m = Py_InitModule("xend.utils", xu_methods);
+
+ d = PyModule_GetDict(m);
+ port_error = PyErr_NewException("xend.utils.PortError", NULL, NULL);
+ PyDict_SetItemString(d, "PortError", port_error);
+}
from distutils.core import setup, Extension
-module = Extension("xend_utils",
- include_dirs = ["../xc/lib",
- "../../xenolinux-sparse/include"],
- library_dirs = ["../xc/lib"],
- libraries = ["xc"],
- sources = ["xend_utils.c"])
+utils = Extension("utils",
+ include_dirs = ["../xc/lib",
+ "../../xenolinux-sparse/include"],
+ library_dirs = ["../xc/lib"],
+ libraries = ["xc"],
+ sources = ["lib/utils.c"])
-setup(name = "xend_utils", version = "1.0", ext_modules = [module])
+setup(name = "xend",
+ version = "1.0",
+ packages = ["xend"],
+ package_dir = { "xend" : "lib" },
+ ext_package = "xend",
+ ext_modules = [ utils ]
+ )
+++ /dev/null
-#!/usr/bin/env python
-
-
-###########################################################
-## xend.py -- Xen controller daemon
-## Copyright (c) 2004, K A Fraser (University of Cambridge)
-###########################################################
-
-
-import errno, re, os, pwd, select, signal, socket, struct, sys, tempfile, time
-import xend_utils, Xc
-
-
-
-# The following parameters could be placed in a configuration file.
-PID = '/var/run/xend.pid'
-LOG = '/var/log/xend.log'
-USER = 'root'
-CONTROL_DIR = '/var/run/xend'
-UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR
-
-
-
-##
-## console_interface:
-## Each control interface owns an instance of this class, which manages
-## the current state of the console interface. Normally a console interface
-## will be one of two state:
-## LISTENING: listening for a connection on TCP port 'self.port'
-## CONNECTED: sending/receiving console data on TCP port 'self.port'
-##
-## A dictionary of all active interfaces, indexed by TCP socket descriptor,
-## is accessible as 'console_interface.interface_list'.
-##
-## NB. When a class instance is to be destroyed you *must* call the 'close'
-## method. Otherwise a stale reference will eb left in the interface list.
-##
-class console_interface:
-
- # The various states that a console interface may be in.
- CLOSED = 0 # No console activity
- LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'.
- CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'.
-
-
- # Dictionary of all active (non-closed) console interfaces.
- interface_list = {}
-
-
- # NB. 'key' is an opaque value that has no meaning in this class.
- def __init__(self, port, key):
- self.status = console_interface.CLOSED
- self.port = port
- self.key = key
-
-
- # Is this interface closed (inactive)?
- def closed(self):
- return self.status == console_interface.CLOSED
-
-
- # Is this interface listening?
- def listening(self):
- return self.status == console_interface.LISTENING
-
-
- # Is this interface active and connected?
- def connected(self):
- return self.status == console_interface.CONNECTED
-
-
- # Close the interface, if it is not closed already.
- def close(self):
- if not self.closed():
- del console_interface.interface_list[self.sock.fileno()]
- self.sock.close()
- del self.sock
- self.status = console_interface.CLOSED
-
-
- # Move the interface into the 'listening' state. Opens a new listening
- # socket and updates 'interface_list'.
- def listen(self):
- # Close old socket (if any), and create a fresh one.
- self.close()
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
-
- try:
- # Turn the new socket into a non-blocking listener.
- self.sock.setblocking(False)
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
- struct.pack('ii', 0, 0))
- self.sock.bind(('', self.port))
- self.sock.listen(1)
-
- # Announce the new status of thsi interface.
- self.status = console_interface.LISTENING
- console_interface.interface_list[self.sock.fileno()] = self
-
- except:
- # In case of trouble ensure we get rid of dangling socket reference
- self.sock.close()
- del self.sock
- raise
-
-
- # Move a listening interface into the 'connected' state.
- def connect(self):
- # Pick up a new connection, if one is available.
- try:
- (sock, addr) = self.sock.accept()
- except:
- return 0
- sock.setblocking(False)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
- struct.pack('ii', 0, 0))
-
- # Close the listening socket.
- self.sock.close()
-
- # Publish the new socket and the new interface state.
- self.sock = sock
- self.status = console_interface.CONNECTED
- console_interface.interface_list[self.sock.fileno()] = self
- return 1
-
-
-
-##
-## new_control_interface:
-## Create a new control interface with the specified domain 'dom'.
-## The console port may also be specified; otehrwise a suitable port is
-## automatically allocated.
-##
-def new_control_interface(dom, console_port=-1):
- # Allocate an event channel. Clear pending notifications.
- port = xend_utils.port(dom)
- notifier.clear(port.local_port, notifier.NORMAL)
- notifier.clear(port.local_port, notifier.DISCONNECT)
-
- # If necessary, compute a suitable TCP port for console I/O.
- if console_port < 0:
- console_port = 9600 + port.local_port
-
- # Create a listenign console interface.
- con_if = console_interface(console_port, port.local_port)
- con_if.listen()
-
- # Add control state to the master list.
- control_list[port.local_port] = \
- (port, xend_utils.buffer(), xend_utils.buffer(), con_if)
-
- # Construct the successful response to be returned to the requester.
- response = { 'success': True }
- response['local_port'] = port.local_port
- response['remote_port'] = port.remote_port
- response['console_port'] = console_port
- return response
-
-
-
-def daemon_loop():
- global control_list, notifier
-
- xc = Xc.new()
- control_list = {}
-
- # Ignore writes to disconnected sockets. We clean up differently.
- signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-
- # Construct the management interface. This is a UNIX domain socket via
- # which we receive 'request' datagrams. Each request is a string that
- # can be eval'ed as a Python statement. Responses can be remotely eval'ed
- # by the requester to create a Python dictionary of result values.
- management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
- if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK):
- os.unlink(CONTROL_DIR+'/'+UNIX_SOCK)
- management_interface.setblocking(False)
- management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK)
-
- notifier = xend_utils.notifier()
-
- ##
- ## MAIN LOOP
- ##
- while 1:
-
- # Construct a poll set. We wait on:
- # 1. Requests on the management interface.
- # 2. Incoming event-channel notifications.
- # Furthermore, for each active control interface:
- # 3. Incoming console data.
- # 4. Space for outgoing console data (if there is data to send).
- waitset = select.poll()
- waitset.register(management_interface, select.POLLIN)
- waitset.register(notifier, select.POLLIN)
- for idx, (port, rbuf, wbuf, con_if) in control_list.items():
- if not con_if.closed():
- pflags = select.POLLIN
- if not rbuf.empty() and con_if.connected():
- pflags = select.POLLIN | select.POLLOUT
- waitset.register(con_if.sock.fileno(), pflags)
-
- # Wait for something to do...
- fdset = waitset.poll()
-
- # Look for messages on the management interface.
- # These should consist of executable Python statements that call
- # well-known management functions (e.g., new_control_interface(dom=9)).
- try:
- data, addr = management_interface.recvfrom(2048)
- except socket.error, error:
- if error[0] != errno.EAGAIN:
- raise
- else:
- if addr:
- # Evaluate the request in an exception-trapping sandbox.
- try:
- print "Mgmt_req[%s]: %s" % (addr, data)
- response = str(eval(data))
-
- except:
- # Catch all exceptions and turn into an error response:
- # status: False
- # error_type: 'exception'
- # exception_type: name of exception type.
- # exception value: textual exception value.
- exc_type, exc_val = sys.exc_info()[:2]
- response = { 'success': False }
- response['error_type'] = 'exception'
- response['exception_type'] = str(exc_type)
- response['exception_value'] = str(exc_val)
- response = str(response)
-
- # Try to send a response to the requester.
- try:
- print "Mgmt_rsp[%s]: %s" % (addr, response)
- management_interface.sendto(response, addr)
- except socket.error, error:
- pass
-
- # Do work for every console interface that hit in the poll set.
- for (fd, events) in fdset:
- if not console_interface.interface_list.has_key(fd):
- continue
- con_if = console_interface.interface_list[fd]
-
- # If the interface is listening, check for pending connections.
- if con_if.listening():
- con_if.connect()
-
- # All done if the interface is not connected.
- if not con_if.connected():
- continue
- (port, rbuf, wbuf, con_if) = control_list[con_if.key]
-
- # Send as much pending data as possible via the socket.
- while not rbuf.empty():
- try:
- bytes = con_if.sock.send(rbuf.peek())
- if bytes > 0:
- rbuf.discard(bytes)
- except socket.error, error:
- pass
-
- # Read as much data as is available. Don't worry about
- # overflowing our buffer: it's more important to read the
- # incoming data stream and detect errors or closure of the
- # remote end in a timely manner.
- try:
- while 1:
- data = con_if.sock.recv(2048)
- # Return of zero means the remote end has disconnected.
- # We therefore return the console interface to listening.
- if not data:
- con_if.listen()
- break
- wbuf.write(data)
- except socket.error, error:
- # Assume that most errors mean that the connection is dead.
- # In such cases we return the interface to 'listening' state.
- if error[0] != errno.EAGAIN:
- print "Better return to listening"
- con_if.listen()
- print "New status: " + str(con_if.status)
-
- # We may now have pending data to send via the relevant
- # inter-domain control interface. If so then we send all we can
- # and notify the remote end.
- work_done = False
- while not wbuf.empty() and port.space_to_write_request():
- msg = xend_utils.message(0, 0, 0)
- msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
- port.write_request(msg)
- work_done = True
- if work_done:
- port.notify()
-
- # Process control-interface notifications from other guest OSes.
- while 1:
- # Grab a notification, if there is one.
- notification = notifier.read()
- if not notification:
- break
- (idx, type) = notification
-
- # If we pick up a disconnect notification then we do any necessary
- # cleanup, even if the event channel doesn't belong to us.
- # This is intended to prevent the event-channel port space from
- # getting clogged with stale connections.
- if type == notifier.DISCONNECT:
- ret = xc.evtchn_status(idx)
- if ret['status'] != 'connected':
- notifier.clear(idx, notifier.NORMAL)
- notifier.clear(idx, notifier.DISCONNECT)
- if control_list.has_key(idx):
- (port, rbuf, wbuf, con_if) = control_list[idx]
- con_if.close()
- del control_list[idx], port, rbuf, wbuf, con_if
- elif ret['status'] == 'disconnected':
- # There's noone to do the closure for us...
- xc.evtchn_close(idx)
-
- # A standard notification: probably means there are messages to
- # read or that there is space to write messages.
- elif type == notifier.NORMAL and control_list.has_key(idx):
- (port, rbuf, wbuf, con_if) = control_list[idx]
- work_done = False
-
- # We clear the notification before doing any work, to avoid
- # races.
- notifier.clear(idx, notifier.NORMAL)
-
- # Read incoming requests. Currently assume that request
- # message always containb console data.
- while port.request_to_read():
- msg = port.read_request()
- rbuf.write(msg.get_payload())
- port.write_response(msg)
- work_done = True
-
- # Incoming responses are currently thrown on the floor.
- while port.response_to_read():
- msg = port.read_response()
- work_done = True
-
- # Send as much pending console data as there is room for.
- while not wbuf.empty() and port.space_to_write_request():
- msg = xend_utils.message(0, 0, 0)
- msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
- port.write_request(msg)
- work_done = True
-
- # Finally, notify the remote end of any work that we did.
- if work_done:
- port.notify()
-
-
-
-def cleanup_daemon(kill=False):
- # No cleanup to do if the PID file is empty.
- if not os.path.isfile(PID) or not os.path.getsize(PID):
- return 0
- # Read the PID of the previous invocation and search active process list.
- pid = open(PID, 'r').read()
- lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
- for line in lines:
- if re.search('^ *' + pid + '.+xend', line):
- if not kill:
- print "Daemon is already running (PID %d)" % int(pid)
- return 1
- # Old daemon is still active: terminate it.
- os.kill(int(pid), 1)
- # Delete the, now stale, PID file.
- os.remove(PID)
- return 0
-
-
-
-def start_daemon():
- if cleanup_daemon(kill=False):
- return 1
-
- if not os.path.exists(CONTROL_DIR):
- os.mkdir(CONTROL_DIR)
-
- # Open log file. Truncate it if non-empty, and request line buffering.
- if os.path.isfile(LOG):
- os.rename(LOG, LOG+'.old')
- logfile = open(LOG, 'w+', 1)
-
- # Detach from TTY.
- os.setsid()
-
- # Set the UID.
- try:
- os.setuid(pwd.getpwnam(USER)[2])
- except KeyError, error:
- print "Error: no such user '%s'" % USER
- return 1
-
- # Ensure that zombie children are automatically reaped.
- xend_utils.autoreap()
-
- # Fork -- parent writes the PID file and exits.
- pid = os.fork()
- if pid:
- pidfile = open(PID, 'w')
- pidfile.write(str(pid))
- pidfile.close()
- return 0
-
- # Close down standard file handles
- try:
- os.close(0) # stdin
- os.close(1) # stdout
- os.close(2) # stderr
- except:
- pass
-
- # Redirect output to log file, then enter the main loop.
- sys.stdout = sys.stderr = logfile
- daemon_loop()
- return 0
-
-
-
-def stop_daemon():
- return cleanup_daemon(kill=True)
-
-
-
-def main():
- xend_utils.autoreap()
- if not sys.argv[1:]:
- print 'usage: %s {start|stop|restart}' % sys.argv[0]
- elif os.fork():
- pid, status = os.wait()
- return status >> 8
- elif sys.argv[1] == 'start':
- return start_daemon()
- elif sys.argv[1] == 'stop':
- return stop_daemon()
- elif sys.argv[1] == 'restart':
- return stop_daemon() or start_daemon()
- else:
- print 'not an option:', sys.argv[1]
- return 1
-
-
-
-if __name__ == '__main__':
- sys.exit(main())
+++ /dev/null
-/******************************************************************************
- * xend_utils.c
- *
- * Copyright (c) 2004, K A Fraser
- */
-
-#include <Python.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <sys/poll.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <signal.h>
-#include <xc.h>
-#include <asm-xeno/control_if.h>
-
-/* NB. The following should be kept in sync with the kernel's evtchn driver. */
-#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
-#define EVTCHN_DEV_MAJOR 10
-#define EVTCHN_DEV_MINOR 200
-#define PORT_NORMAL 0x0000 /* A standard event notification. */
-#define PORT_DISCONNECT 0x8000 /* A port-disconnect notification. */
-#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */
-#define EVTCHN_RESET _IO('E', 1) /* Clear notification buffer. Clear errors. */
-
-/* Size of a machine page frame. */
-#define PAGE_SIZE 4096
-
-
-/*
- * *********************** NOTIFIER ***********************
- */
-
-typedef struct {
- PyObject_HEAD;
- int evtchn_fd;
-} xu_notifier_object;
-
-static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
-{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- u16 v;
- int bytes;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
- {
- if ( errno == EINTR )
- continue;
- if ( errno == EAGAIN )
- goto none;
- return PyErr_SetFromErrno(PyExc_IOError);
- }
-
- if ( bytes == sizeof(v) )
- return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK);
-
- none:
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_notifier_clear(PyObject *self, PyObject *args)
-{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- u16 v;
- int idx, type;
-
- if ( !PyArg_ParseTuple(args, "ii", &idx, &type) )
- return NULL;
-
- v = (u16)idx | (u16)type;
-
- (void)write(xun->evtchn_fd, &v, sizeof(v));
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
-{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- return PyInt_FromLong(xun->evtchn_fd);
-}
-
-static PyMethodDef xu_notifier_methods[] = {
- { "read",
- (PyCFunction)xu_notifier_read,
- METH_VARARGS,
- "Read a (@port, @type) pair.\n" },
-
- { "clear",
- (PyCFunction)xu_notifier_clear,
- METH_VARARGS,
- "Clear a (@port, @type) pair.\n" },
-
- { "fileno",
- (PyCFunction)xu_notifier_fileno,
- METH_VARARGS,
- "Return the file descriptor for the notification channel.\n" },
-
- { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_notifier_type;
-
-static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
-{
- xu_notifier_object *xun;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
-
- xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
- if ( xun->evtchn_fd == -1 )
- {
- PyObject_Del((PyObject *)xun);
- return PyErr_SetFromErrno(PyExc_IOError);
- }
-
- return (PyObject *)xun;
-}
-
-static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
-{
- if ( strcmp(name, "DISCONNECT") == 0 )
- return PyInt_FromLong(PORT_DISCONNECT);
- if ( strcmp(name, "NORMAL") == 0 )
- return PyInt_FromLong(PORT_NORMAL);
- return Py_FindMethod(xu_notifier_methods, obj, name);
-}
-
-static void xu_notifier_dealloc(PyObject *self)
-{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- (void)close(xun->evtchn_fd);
- PyObject_Del(self);
-}
-
-static PyTypeObject xu_notifier_type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- "notifier",
- sizeof(xu_notifier_object),
- 0,
- xu_notifier_dealloc, /* tp_dealloc */
- NULL, /* tp_print */
- xu_notifier_getattr, /* tp_getattr */
- NULL, /* tp_setattr */
- NULL, /* tp_compare */
- NULL, /* tp_repr */
- NULL, /* tp_as_number */
- NULL, /* tp_as_sequence */
- NULL, /* tp_as_mapping */
- NULL /* tp_hash */
-};
-
-
-
-/*
- * *********************** MESSAGE ***********************
- */
-
-typedef struct {
- PyObject_HEAD;
- control_msg_t msg;
-} xu_message_object;
-
-static PyObject *xu_message_append_payload(PyObject *self, PyObject *args)
-{
- xu_message_object *xum = (xu_message_object *)self;
- char *str;
- int len;
-
- if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
- return NULL;
-
- if ( (len + xum->msg.length) > sizeof(xum->msg.msg) )
- {
- PyErr_SetString(PyExc_RuntimeError, "out of space in control message");
- return NULL;
- }
-
- memcpy(&xum->msg.msg[xum->msg.length], str, len);
- xum->msg.length += len;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
-{
- xu_message_object *xum = (xu_message_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length);
-}
-
-static PyObject *xu_message_set_header(PyObject *self,
- PyObject *args,
- PyObject *kwds)
-{
- xu_message_object *xum = (xu_message_object *)self;
- int type = -1, subtype = -1, id = -1;
-
- static char *kwd_list[] = { "type", "subtype", "id", NULL };
-
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list,
- &type, &subtype, &id) )
- return NULL;
-
- if ( type != -1 )
- xum->msg.type = (u8)type;
- if ( subtype != -1 )
- xum->msg.subtype = (u8)subtype;
- if ( id != -1 )
- xum->msg.id = (u8)id;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_message_get_header(PyObject *self, PyObject *args)
-{
- xu_message_object *xum = (xu_message_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- return Py_BuildValue("{s:i,s:i,s:i}",
- "type", xum->msg.type,
- "subtype", xum->msg.subtype,
- "id", xum->msg.id);
-}
-
-static PyMethodDef xu_message_methods[] = {
- { "append_payload",
- (PyCFunction)xu_message_append_payload,
- METH_VARARGS,
- "Append @str to the message payload.\n" },
-
- { "get_payload",
- (PyCFunction)xu_message_get_payload,
- METH_VARARGS,
- "Return the message payload in string form.\n" },
-
- { "set_header",
- (PyCFunction)xu_message_set_header,
- METH_VARARGS | METH_KEYWORDS,
- "Accepts keywords @type, @subtype, and @id.\n" },
-
- { "get_header",
- (PyCFunction)xu_message_get_header,
- METH_VARARGS,
- "Returns a dictionary of values for @type, @subtype, and @id.\n" },
-
- { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_message_type;
-
-static PyObject *xu_message_new(PyObject *self, PyObject *args)
-{
- xu_message_object *xum;
- int type, subtype, id;
-
- if ( !PyArg_ParseTuple(args, "iii", &type, &subtype, &id) )
- return NULL;
-
- xum = PyObject_New(xu_message_object, &xu_message_type);
-
- xum->msg.type = type;
- xum->msg.subtype = subtype;
- xum->msg.id = id;
- xum->msg.length = 0;
-
- return (PyObject *)xum;
-}
-
-static PyObject *xu_message_getattr(PyObject *obj, char *name)
-{
- xu_message_object *xum;
- if ( strcmp(name, "MAX_PAYLOAD") == 0 )
- return PyInt_FromLong(sizeof(xum->msg.msg));
- return Py_FindMethod(xu_message_methods, obj, name);
-}
-
-static void xu_message_dealloc(PyObject *self)
-{
- PyObject_Del(self);
-}
-
-static PyTypeObject xu_message_type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- "message",
- sizeof(xu_message_object),
- 0,
- xu_message_dealloc, /* tp_dealloc */
- NULL, /* tp_print */
- xu_message_getattr, /* tp_getattr */
- NULL, /* tp_setattr */
- NULL, /* tp_compare */
- NULL, /* tp_repr */
- NULL, /* tp_as_number */
- NULL, /* tp_as_sequence */
- NULL, /* tp_as_mapping */
- NULL /* tp_hash */
-};
-
-
-
-/*
- * *********************** PORT ***********************
- */
-
-static control_if_t *map_control_interface(int fd, unsigned long pfn)
-{
- char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_SHARED, fd, pfn * PAGE_SIZE);
- if ( vaddr == MAP_FAILED )
- return NULL;
- return (control_if_t *)(vaddr + 2048);
-}
-static void unmap_control_interface(int fd, control_if_t *c)
-{
- char *vaddr = (char *)c - 2048;
- (void)munmap(vaddr, PAGE_SIZE);
-}
-
-typedef struct {
- PyObject_HEAD;
- int mem_fd;
- int xc_handle;
- u64 remote_dom;
- int local_port, remote_port;
- control_if_t *interface;
- CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
- CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
-} xu_port_object;
-
-static PyObject *port_error;
-
-static PyObject *xu_port_notify(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- xu_message_object *xum;
- CONTROL_RING_IDX c = xup->tx_req_cons;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->tx_req_prod) ||
- ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
- {
- PyErr_SetString(port_error, "no request to read");
- return NULL;
- }
-
- cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
- xum = PyObject_New(xu_message_object, &xu_message_type);
- memcpy(&xum->msg, cmsg, sizeof(*cmsg));
- if ( xum->msg.length > sizeof(xum->msg.msg) )
- xum->msg.length = sizeof(xum->msg.msg);
- xup->tx_req_cons++;
- return (PyObject *)xum;
-}
-
-static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- xu_message_object *xum;
- CONTROL_RING_IDX p = xup->rx_req_prod;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
- return NULL;
-
- if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
- {
- PyErr_SetString(PyExc_TypeError, "expected a xend_utils.message");
- return NULL;
- }
-
- if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
- {
- PyErr_SetString(port_error, "no space to write request");
- return NULL;
- }
-
- cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
- memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
- xup->rx_req_prod = cif->rx_req_prod = p + 1;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- xu_message_object *xum;
- CONTROL_RING_IDX c = xup->rx_resp_cons;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
- {
- PyErr_SetString(port_error, "no response to read");
- return NULL;
- }
-
- cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
- xum = PyObject_New(xu_message_object, &xu_message_type);
- memcpy(&xum->msg, cmsg, sizeof(*cmsg));
- if ( xum->msg.length > sizeof(xum->msg.msg) )
- xum->msg.length = sizeof(xum->msg.msg);
- xup->rx_resp_cons++;
- return (PyObject *)xum;
-}
-
-static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- xu_message_object *xum;
- CONTROL_RING_IDX p = xup->tx_resp_prod;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
- return NULL;
-
- if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
- {
- PyErr_SetString(PyExc_TypeError, "expected a xend_utils.message");
- return NULL;
- }
-
- if ( p == xup->tx_req_cons )
- {
- PyErr_SetString(port_error, "no space to write response");
- return NULL;
- }
-
- cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
- memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
- xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX c = xup->tx_req_cons;
- control_if_t *cif = xup->interface;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->tx_req_prod) ||
- ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX p = xup->rx_req_prod;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX c = xup->rx_resp_cons;
- control_if_t *cif = xup->interface;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_space_to_write_response(
- PyObject *self, PyObject *args)
-{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX p = xup->tx_resp_prod;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( p == xup->tx_req_cons )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
-}
-
-static PyMethodDef xu_port_methods[] = {
- { "notify",
- (PyCFunction)xu_port_notify,
- METH_VARARGS,
- "Send a notification to the remote end.\n" },
-
- { "read_request",
- (PyCFunction)xu_port_read_request,
- METH_VARARGS,
- "Read a request message from the control interface.\n" },
-
- { "write_request",
- (PyCFunction)xu_port_write_request,
- METH_VARARGS,
- "Write a request message to the control interface.\n" },
-
- { "read_response",
- (PyCFunction)xu_port_read_response,
- METH_VARARGS,
- "Read a response message from the control interface.\n" },
-
- { "write_response",
- (PyCFunction)xu_port_write_response,
- METH_VARARGS,
- "Write a response message to the control interface.\n" },
-
- { "request_to_read",
- (PyCFunction)xu_port_request_to_read,
- METH_VARARGS,
- "Returns TRUE if there is a request message to read.\n" },
-
- { "space_to_write_request",
- (PyCFunction)xu_port_space_to_write_request,
- METH_VARARGS,
- "Returns TRUE if there is space to write a request message.\n" },
-
- { "response_to_read",
- (PyCFunction)xu_port_response_to_read,
- METH_VARARGS,
- "Returns TRUE if there is a response message to read.\n" },
-
- { "space_to_write_response",
- (PyCFunction)xu_port_space_to_write_response,
- METH_VARARGS,
- "Returns TRUE if there is space to write a response message.\n" },
-
- { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_port_type;
-
-static PyObject *xu_port_new(PyObject *self, PyObject *args)
-{
- xu_port_object *xup;
- u64 dom;
- int port1, port2;
- xc_dominfo_t info;
-
- if ( !PyArg_ParseTuple(args, "L", &dom) )
- return NULL;
-
- xup = PyObject_New(xu_port_object, &xu_port_type);
-
- if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
- {
- PyErr_SetString(port_error, "Could not open '/dev/mem'");
- goto fail1;
- }
-
- if ( (xup->xc_handle = xc_interface_open()) == -1 )
- {
- PyErr_SetString(port_error, "Could not open Xen control interface");
- goto fail2;
- }
-
- if ( xc_evtchn_open(xup->xc_handle, DOMID_SELF, dom, &port1, &port2) != 0 )
- {
- PyErr_SetString(port_error, "Could not open channel to domain");
- goto fail3;
- }
-
- if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) ||
- (info.domid != dom) )
- {
- PyErr_SetString(port_error, "Failed to obtain domain status");
- goto fail4;
- }
-
- xup->interface =
- map_control_interface(xup->mem_fd, info.shared_info_frame);
- if ( xup->interface == NULL )
- {
- PyErr_SetString(port_error, "Failed to map domain control interface");
- goto fail4;
- }
-
- xup->tx_req_cons = 0;
- xup->tx_resp_prod = 0;
- xup->rx_req_prod = 0;
- xup->rx_resp_cons = 0;
- xup->remote_dom = dom;
- xup->local_port = port1;
- xup->remote_port = port2;
-
- return (PyObject *)xup;
-
-
- fail4:
- (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
- fail3:
- (void)xc_interface_close(xup->xc_handle);
- fail2:
- (void)close(xup->mem_fd);
- fail1:
- PyObject_Del((PyObject *)xup);
- return NULL;
-}
-
-static PyObject *xu_port_getattr(PyObject *obj, char *name)
-{
- xu_port_object *xup = (xu_port_object *)obj;
- if ( strcmp(name, "local_port") == 0 )
- return PyInt_FromLong(xup->local_port);
- if ( strcmp(name, "remote_port") == 0 )
- return PyInt_FromLong(xup->remote_port);
- if ( strcmp(name, "remote_dom") == 0 )
- return PyLong_FromUnsignedLongLong(xup->remote_dom);
- return Py_FindMethod(xu_port_methods, obj, name);
-}
-
-static void xu_port_dealloc(PyObject *self)
-{
- xu_port_object *xup = (xu_port_object *)self;
- unmap_control_interface(xup->mem_fd, xup->interface);
- (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
- (void)xc_interface_close(xup->xc_handle);
- (void)close(xup->mem_fd);
- PyObject_Del(self);
-}
-
-static PyTypeObject xu_port_type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- "port",
- sizeof(xu_port_object),
- 0,
- xu_port_dealloc, /* tp_dealloc */
- NULL, /* tp_print */
- xu_port_getattr, /* tp_getattr */
- NULL, /* tp_setattr */
- NULL, /* tp_compare */
- NULL, /* tp_repr */
- NULL, /* tp_as_number */
- NULL, /* tp_as_sequence */
- NULL, /* tp_as_mapping */
- NULL /* tp_hash */
-};
-
-
-
-/*
- * *********************** BUFFER ***********************
- */
-
-#define BUFSZ 65536
-#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1))
-typedef unsigned int BUF_IDX;
-
-typedef struct {
- PyObject_HEAD;
- char *buf;
- unsigned int prod, cons;
-} xu_buffer_object;
-
-static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max)
-{
- PyObject *str1, *str2;
- int len1, len2, c = MASK_BUF_IDX(xub->cons);
-
- len1 = xub->prod - xub->cons;
- if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */
- len1 = BUFSZ - c;
- if ( len1 > max ) /* clip to specified maximum */
- len1 = max;
- if ( len1 < 0 ) /* sanity */
- len1 = 0;
-
- if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL )
- return NULL;
-
- if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) )
- {
- len2 = max - len1;
- if ( len2 > MASK_BUF_IDX(xub->prod) )
- len2 = MASK_BUF_IDX(xub->prod);
- if ( len2 > 0 )
- {
- str2 = PyString_FromStringAndSize(&xub->buf[0], len2);
- if ( str2 == NULL )
- return NULL;
- PyString_ConcatAndDel(&str1, str2);
- if ( str1 == NULL )
- return NULL;
- }
- }
-
- return str1;
-}
-
-static PyObject *xu_buffer_peek(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
- int max = 1024;
-
- if ( !PyArg_ParseTuple(args, "|i", &max) )
- return NULL;
-
- return __xu_buffer_peek(xub, max);
-}
-
-static PyObject *xu_buffer_read(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
- PyObject *str;
- int max = 1024;
-
- if ( !PyArg_ParseTuple(args, "|i", &max) )
- return NULL;
-
- if ( (str = __xu_buffer_peek(xub, max)) != NULL )
- xub->cons += PyString_Size(str);
-
- return str;
-}
-
-static PyObject *xu_buffer_discard(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
- int max, len;
-
- if ( !PyArg_ParseTuple(args, "i", &max) )
- return NULL;
-
- len = xub->prod - xub->cons;
- if ( len > max )
- len = max;
- if ( len < 0 )
- len = 0;
-
- xub->cons += len;
-
- return PyInt_FromLong(len);
-}
-
-static PyObject *xu_buffer_write(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
- char *str;
- int len, len1, len2;
-
- if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
- return NULL;
-
- len1 = len;
- if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
- len1 = BUFSZ - MASK_BUF_IDX(xub->prod);
- if ( len1 > (BUFSZ - (xub->prod - xub->cons)) )
- len1 = BUFSZ - (xub->prod - xub->cons);
-
- if ( len1 == 0 )
- return PyInt_FromLong(0);
-
- memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1);
- xub->prod += len1;
-
- if ( len1 < len )
- {
- len2 = len - len1;
- if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
- len2 = BUFSZ - MASK_BUF_IDX(xub->prod);
- if ( len2 > (BUFSZ - (xub->prod - xub->cons)) )
- len2 = BUFSZ - (xub->prod - xub->cons);
- if ( len2 != 0 )
- {
- memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2);
- xub->prod += len2;
- return PyInt_FromLong(len1 + len2);
- }
- }
-
- return PyInt_FromLong(len1);
-}
-
-static PyObject *xu_buffer_empty(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( xub->cons == xub->prod )
- return PyInt_FromLong(1);
-
- return PyInt_FromLong(0);
-}
-
-static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (xub->prod - xub->cons) == BUFSZ )
- return PyInt_FromLong(1);
-
- return PyInt_FromLong(0);
-}
-
-static PyMethodDef xu_buffer_methods[] = {
- { "peek",
- (PyCFunction)xu_buffer_peek,
- METH_VARARGS,
- "Peek up to @max bytes from the buffer. Returns a string.\n" },
-
- { "read",
- (PyCFunction)xu_buffer_read,
- METH_VARARGS,
- "Read up to @max bytes from the buffer. Returns a string.\n" },
-
- { "discard",
- (PyCFunction)xu_buffer_discard,
- METH_VARARGS,
- "Discard up to @max bytes from the buffer. Returns number of bytes.\n" },
-
- { "write",
- (PyCFunction)xu_buffer_write,
- METH_VARARGS,
- "Write @string into buffer. Return number of bytes written.\n" },
-
- { "empty",
- (PyCFunction)xu_buffer_empty,
- METH_VARARGS,
- "Return TRUE if the buffer is empty.\n" },
-
- { "full",
- (PyCFunction)xu_buffer_full,
- METH_VARARGS,
- "Return TRUE if the buffer is full.\n" },
-
- { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_buffer_type;
-
-static PyObject *xu_buffer_new(PyObject *self, PyObject *args)
-{
- xu_buffer_object *xub;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- xub = PyObject_New(xu_buffer_object, &xu_buffer_type);
-
- if ( (xub->buf = malloc(BUFSZ)) == NULL )
- {
- PyObject_Del((PyObject *)xub);
- return NULL;
- }
-
- xub->prod = xub->cons = 0;
-
- return (PyObject *)xub;
-}
-
-static PyObject *xu_buffer_getattr(PyObject *obj, char *name)
-{
- return Py_FindMethod(xu_buffer_methods, obj, name);
-}
-
-static void xu_buffer_dealloc(PyObject *self)
-{
- xu_buffer_object *xub = (xu_buffer_object *)self;
- free(xub->buf);
- PyObject_Del(self);
-}
-
-static PyTypeObject xu_buffer_type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- "buffer",
- sizeof(xu_buffer_object),
- 0,
- xu_buffer_dealloc, /* tp_dealloc */
- NULL, /* tp_print */
- xu_buffer_getattr, /* tp_getattr */
- NULL, /* tp_setattr */
- NULL, /* tp_compare */
- NULL, /* tp_repr */
- NULL, /* tp_as_number */
- NULL, /* tp_as_sequence */
- NULL, /* tp_as_mapping */
- NULL /* tp_hash */
-};
-
-
-
-/*
- * *********************** MODULE WRAPPER ***********************
- */
-
-static void handle_child_death(int dummy)
-{
- while ( waitpid(-1, NULL, WNOHANG) > 0 )
- continue;
-}
-
-static PyObject *xu_autoreap(PyObject *self, PyObject *args)
-{
- struct sigaction sa;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = handle_child_death;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
- (void)sigaction(SIGCHLD, &sa, NULL);
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyMethodDef xu_methods[] = {
- { "notifier", xu_notifier_new, METH_VARARGS,
- "Create a new notifier." },
- { "message", xu_message_new, METH_VARARGS,
- "Create a new communications message." },
- { "port", xu_port_new, METH_VARARGS,
- "Create a new communications port." },
- { "buffer", xu_buffer_new, METH_VARARGS,
- "Create a new ring buffer." },
- { "autoreap", xu_autoreap, METH_VARARGS,
- "Ensure that zombie children are automatically reaped by the OS." },
- { NULL, NULL, 0, NULL }
-};
-
-PyMODINIT_FUNC initxend_utils(void)
-{
- PyObject *m, *d;
-
- m = Py_InitModule("xend_utils", xu_methods);
-
- d = PyModule_GetDict(m);
- port_error = PyErr_NewException("xend_utils.PortError", NULL, NULL);
- PyDict_SetItemString(d, "PortError", port_error);
-}